<?php
/**
 * Created by PhpStorm.
 * User: user1
 * Date: 2018/9/3
 * Time: 11:33
 */

namespace app\common\service;


use think\Db;
use think\Exception;
use think\facade\Cache;
use think\facade\Request;
use app\common\model\Auth as AuthModel;
use app\common\model\Role as RoleModel;
use app\common\model\Admin as AdminModel;

class JurisdictionService
{
    private static $model;

    /**
     * 验证管理员登录权限
     * @param $admin
     * @throws Exception
     * @throws \think\exception\DbException
     */
    public static function checkAdminLoginJurisdiction($admin) {
        if (!$admin) {
            throw new Exception('请先登录');
        }

        $controller = Request::controller(); // 访问的控制器
        $action = Request::action(); // 访问的方法

        $auth = AuthModel::get(['controller' => $controller, 'action' => $action]); // 获取访问路径对应的权限信息
//        dump($auth);exit;
        if (!$auth) {
            throw new Exception('没有访问权限');
        }
//        RoleAuthModel::destroy(1);
        if (!$jurisdiction = cache('szb_admin_' . $admin['id'] . '_jurisdiction')) {
            // 创建管理员权限缓存
            $jurisdiction = self::createAdminJurisdictionCache($admin['role_ids'], $admin['id']);
        }
        $jurisdiction = explode(',', $jurisdiction);

        if (!in_array($auth->id, $jurisdiction)) {  // 登录用户是否拥有访问权限
            throw new Exception('权限验证失败');
        }
    }

    /**
     * 获取管理员的导航数据
     * @param $admin
     * @return mixed
     * @throws Exception
     * @throws \think\exception\DbException
     */
    public static function getAdminShowMenu($admin) {
        if (!$menu = cache('szb_admin_' . $admin['id'] . '_menu')) {
            $auths = (AuthModel::order('order desc, id desc')->select())->toArray(); // 获取所有权限
            $auths = self::list_to_tree($auths, 'id', 'parent_id'); // 将分类列表数据转换为无限极树状结构
            $jurisdiction = explode(',', self::getAdminJurisdiction($admin)); // 管理员已有权限
            $menu = json_encode(self::getMenuByAdminJurisdiction($auths, $jurisdiction)); // 根据权限获取导航数据
            cache('szb_admin_' . $admin['id'] . '_menu');
        }
//        print_R(json_decode($menu, true));die;
        return json_decode($menu, true);
    }

    /**
     * 根据条件获取权限数据
     * @param int $role_id
     * @param string $authIds
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public static function getAllAuth($role_id = 0, $authIds = '') {
        $auths = (AuthModel::where(function ($query) use($authIds) {
            if($authIds) {
                $query->where('id', 'in', $authIds);
            }
        })->order('order desc, id desc')->select())->toArray(); // 获取所有权限
        if ($role_id > 0) {
            // 获取当前角色已有的权限
            $roleAuthIds = RoleModel::getAdminAuthIds($role_id);

            // 检查权限是否需要选中
            self::checkAuthIsChecked($auths, $roleAuthIds);
        }

        $auths = self::list_to_tree($auths, 'id', 'parent_id'); // 将分类列表数据转换为无限极树状结构
        return $auths;
    }

    /**
     * 检查权限是否需要被选中
     * @param $auths
     * @param $authIds
     */
    private static function checkAuthIsChecked(&$auths, $authIds) {
        $authIds = explode(',', $authIds);
        foreach ($auths as &$v) {
            if (in_array($v['id'], $authIds)) {
                $v['checked'] = 1;
            } else {
                $v['checked'] = 0;
            }
        }
    }

    /**
     * 把返回的数据集转换成Tree
     * @param array $list 要转换的数据集
     * @param string $pid parent标记字段
     * @param string $level level标记字段
     * @return array
     * @author 麦当苗儿 <zuojiazi@vip.qq.com>
     */
    public static function list_to_tree($list, $pk='id', $pid = 'pid', $child = 'list', $root = 0) {
        // 创建Tree
        $tree = array();
        if(is_array($list)) {
            // 创建基于主键的数组引用
            $refer = array();
            foreach ($list as $key => $data) {
                $refer[$data[$pk]] =& $list[$key];
            }
            foreach ($list as $key => $data) {
                // 判断是否存在parent
                $parentId =  $data[$pid];
                if ($root == $parentId) {
                    $tree[] =& $list[$key];
                }else{
                    if (isset($refer[$parentId])) {
                        $parent =& $refer[$parentId];
                        $parent[$child][] =& $list[$key];
                    }
                }
            }
        }
        return $tree;
    }

    /**
     * 根据父级ID获取权限列表，并且可以通过 $noid 过滤掉某个权限和他的下级权限
     * @param int $pid
     * @param int $level
     * @param int $noid
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public static function getAuths($pid = 0, $level = 0, $noid = 0)
    {
        if (!self::$model)  {
            self::$model = new AuthModel();
        }

        static $res = array();// static 定义函数内部的全局变量; 当函数内部调用该函数本身时，所得数据需要该定义才能追加保存到同一变量；
        $data = self::$model->where([['parent_id', '=', $pid], ['id', 'neq', $noid]])->order('order desc, id desc')->select();
        //print_r($data);die;
        foreach($data as $v)
        {
            $v['level'] = $level;	//记录级别
            $v['authNameLevel'] = str_repeat('&nbsp;', $v['level'] * 6) . $v['name'];
            $res[] = $v;
            //echo "<p>$v[catename]</p>";
            self::getAuths($v['id'], $level+1, $noid);	//函数内部调用该函数本身
        }

        return $res;
    }

    /**
     * 根据管理员权限获取导航数据
     * @param $auths
     * @param $jurisdiction
     * @return mixed
     */
    public static function getMenuByAdminJurisdiction($auths, $jurisdiction) {
        foreach ($auths as $k => $v) {
            if ($v['controller'] == 'Index' && $v['action'] == 'index') {
                // 主框架权限不显示在导航
                unset($auths[$k]);
                continue;
            }

            if (isset($v['list'])) {
                foreach ($v['list'] as $lk => $lv) {
                    if (!in_array($lv['id'], $jurisdiction)) {
                        unset($auths[$k]['list'][$lk]);
                    }
                }
            }
            if ((!isset($v['list']) || !$auths[$k]['list']) && !$v['controller'] && !$v['action']) {
                unset($auths[$k]);
            }
        }

        return $auths;
    }

    /**
     * 创建管理员权限缓存
     * @param $role_ids
     * @param $admin_id
     * @return string
     */
    public static function createAdminJurisdictionCache($role_ids, $admin_id) {

        $authIds = RoleModel::getAdminAuthIds($role_ids);

        // 权限信息写入缓存
        cache('szb_admin_' . $admin_id . '_jurisdiction', $authIds);

        return $authIds;
    }

    /**
     * 获取当前管理员的权限
     * @param $userid
     * @return mixed
     * @throws Exception
     * @throws \think\exception\DbException
     */
    public static function getAdminJurisdiction($admin) {
        if ( !$jurisdiction = cache('szb_admin_' . $admin['id'] . '_jurisdiction')) {
            $jurisdiction = self::createAdminJurisdictionCache($admin['role_ids'], $admin['id']);
        }
        return $jurisdiction;
    }

    /**
     * 清除角色下面的用户的权限缓存
     * @param $role_id
     * @throws \think\exception\DbException
     */
    public static function clearAdminJurisdictionCacheByRole($role_id) {
        $list = AdminModel::all(function ($query) use ($role_id){
            $query->where(Db::raw("find_in_set($role_id, 'role_ids')"));
        });
        foreach ($list as $v) {
            cache('szb_admin_' . $v->id . '_jurisdiction', null);
            cache('szb_admin_' . $v->id . '_menu', null);
        }
    }

    /**
     * 设置导航的url
     * @param $menu
     */
    public static function setMenuUrl(&$menu) {
        Cache::clear();
        foreach ($menu as &$value) {
            if(isset($value['controller']) && isset($value['action'])) {
                $value['url'] = url($value['controller'] . '/' . $value['action']);
            }
            if (isset($value['list'])) {
                foreach ($value['list'] as &$v) {
                    $v['url'] = url($v['controller'] . '/' . $v['action']);
                }
            }
        }
    }
}